package org.kolonitsky.css.cssdoc;

import org.apache.commons.io.FileUtils;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;

import static org.apache.commons.io.FileUtils.getFile;
import static org.apache.commons.io.FilenameUtils.getBaseName;

/**
 * @author Alex.Kolonitsky
 */
public class XmlToCssDoc {

    public static final String FILES_INDEX_XML = ".files-index.xml";

    private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();

    private final File xsltDir;
    private final File htmlDir;
    private final File indexXmlFile;

    public XmlToCssDoc(File xsltDir, File htmlDir) {
        this.xsltDir = xsltDir;
        this.htmlDir = htmlDir;
        this.indexXmlFile = getFile(this.htmlDir, FILES_INDEX_XML);
    }

    public void generateReports(Collection<File> cssDocXmlFiles) {
        Collection<File> xsltFiles = findAllXslt();

        generatePerFilesReports(filterByPrefix(xsltFiles, "file"), cssDocXmlFiles);

        generateXmlFilesIndex(cssDocXmlFiles);
        generateIndexReports(filterByPrefix(xsltFiles, "index"));
    }

    private void generatePerFilesReports(Collection<File> xsltFiles, Collection<File> cssFiles) {
        for (File xsltFile : xsltFiles) {
            String fileNameTemplate = inferFileNameTemplate(xsltFile);

            for (File cssFile : cssFiles) {
                generateReport(xsltFile, cssFile, getFile(htmlDir, inferReportName(fileNameTemplate, cssFile)));
            }
        }
    }

    private String inferReportName(String fileNameTemplate, File cssFile) {
        return String.format(fileNameTemplate, getBaseName(cssFile.getPath()));
    }

    private String inferFileNameTemplate(File xsltFile) {
        return getBaseName(xsltFile.getPath()).replace("file-", "").replace("[name]", "%s") + ".html";
    }

    private void generateIndexReports(Collection<File> xsltFiles) {
        for (File xsltFile : xsltFiles) {
            generateReport(xsltFile, indexXmlFile, inferIndexReportFile(xsltFile));
        }
    }

    private File inferIndexReportFile(File xsltFile) {
        return getFile(htmlDir, getBaseName(xsltFile.getPath()).replace("index-", "") + "html");
    }

    private void generateXmlFilesIndex(Collection<File> xmlFiles) {
        try {
            XMLOutputFactory output = XMLOutputFactory.newInstance();
            XMLStreamWriter writer = output.createXMLStreamWriter(new FileWriter(indexXmlFile));

            generateXmlFilesIndex(writer, xmlFiles);

            writer.flush();
            writer.close();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void generateXmlFilesIndex(XMLStreamWriter writer, Collection<File> xmlFiles) throws XMLStreamException {
        writer.writeStartElement("files");
        for (File xmlFile : xmlFiles) {
            writer.writeEmptyElement("file");
            writer.writeAttribute("path", xmlFile.getPath());
        }
        writer.writeEndElement();
    }

    protected void generateReport(File xslt, File inputXml, File outputHtml) {
        try {
            Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslt));

            transformer.transform(new StreamSource(inputXml), new StreamResult(outputHtml));
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }

    protected Collection<File> findAllXslt() {
        return FileUtils.listFiles(this.xsltDir, new String[]{"xslt", "xsl"}, true);
    }

    protected Collection<File> filterByPrefix(Collection<File> files, String prefix) {
        Collection<File> res = new ArrayList<File>(files.size());
        for (File file : files) {
            if (getBaseName(file.getName()).startsWith(prefix)) {
                res.add(file);
            }
        }
        return res;
    }
}
